Fix Swift runtime traps on inject/eject (zstd + MachOKit paths)#101
Fix Swift runtime traps on inject/eject (zstd + MachOKit paths)#101moxcomic wants to merge 4 commits intoLessica:mainfrom
Conversation
Data.append(contentsOf: ArraySlice<UInt8>) starting from an empty _NSZeroData-backed Data triggered a brk #1 in ObjC-bridged value copy during injection. Switch to raw UnsafeMutableRawPointer + the UnsafePointer-based Data.append to avoid the Sequence/COW path, and harden the loop: break cleanly on streamResult == 0 and fail fast on stalled progress instead of potentially spinning on truncated input. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
frameworkMachOsInBundle's fallback loop (added in 8a832b4) calls isMachO on every level-2 item inside Frameworks/*.framework/. For non-Mach-O files — Info.plist, .car, .nib, .bin — MachOKit.loadFromFile reaches an internal NSFileHandle.read<A>(offset:swapHandler:) that triggers a Swift runtime trap (brk #1) instead of throwing, so the try? at the call site cannot catch it. Eject ends up crashing inside Bundle.swift:72 on any app whose frameworks contain non-Mach-O files. Gate isMachO with a cheap file-size + magic-bytes pre-check so non-Mach-O files are rejected before MachOKit is invoked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
frameworkMachOsInBundle's injectedAssetNames loop (added 5ea814a) calls loadedDylibsOfMachO on every Mach-O with a .troll-fools.bak sibling, then iterates MachOKit's loadCommands. On some binaries this reaches LoadCommandsProtocol.infos<A>(of:) → DyldCache.programsTrieEntries → Sequence.programOffsets and triggers a Swift runtime trap (brk #1) that try? cannot catch, killing eject. Stop routing eject through frameworkMachOsInBundle. Add a dedicated collectModifiedMachOs that does a plain Frameworks/ scan for files with a .bak sibling, avoiding every MachOKit load-command path. Also tighten isMachO to a magic-byte check only. MachOKit.loadFromFile reaches the same DyldCache code on some inputs; the 4-byte magic is a sufficient classifier for scan-time filtering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds bilingual CHANGELOG entry and a README summary covering the three Swift runtime traps fixed on top of 4.3 Build 246: the zstd streaming COW trap on the inject path, MachOKit traps on non-Mach-O files, and the MachOKit DyldCache trap on the eject path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
我注意到修复说明中指出的三种 Crash 都是 举个例子,所谓第二个问题说 MachOKit 走 其余问题我先不细看,在你没有给出这三份崩溃的 real world proof (崩溃日志和复现方法)之前,此 PR 不予合入。 |
从4.1-219之后的版本在我的iPad 2022设备上注入/移除注入均直接让TrollFools直接闪退,然后我反复验证并确认了219之后的两个版本在我的设备上都会闪退,已经持续了很久了,恰好今天想起来了问了一下AI,目前在我的设备上已经恢复正常使用 目前并不确认是不是设备的单独问题以及在别的设备是否有这个问题 |
|
第一条与注入有关,说 zstd 逻辑有问题,但是 zstd 是前两天刚加的,还停留在开发分支,并没有带到任何正式版里面去。你又说 4.1-219 之后的版本在你设备上会闪退,说明你这个闪退和第一条指出的所谓「缺陷」没有任何关系。 第二、第三条与推出有关,但指出的都是在 4.3-426 才引入的修改,不应该和你 4.1-219 上的闪退有关系。 总之这三条和你所说的崩溃可以说是毫无关联。这条 PR 幻觉比较重先关了,崩溃日志具体原因有空再看。 |
|
已确认 MachOKit 当中存在一处潜在问题,已在 MachOKit 分支中修复。但不确定是否和你给的崩溃日志之间的关联。后续版本会带上 |
Summary
Fixes three independent Swift runtime traps (
brk #1) that surfaced between4.2-225and4.3-246and do not propagate throughtry?(they are runtime aborts, not thrown errors), so they killed inject/eject end-to-end on affected apps.isMachO= magic-byte check onlycollectModifiedMachOs, no load-command walkVerified end-to-end on iOS 16 (iPad14,5): inject + eject both succeed on top of 4.3-246 with these four commits applied.
Root cause and fix, per bug
1) Inject — zstd streaming decompression (regression from 9e2fcaa)
ZStd.decompressinInjectorV3+Preprocess.swiftstarted from an emptyData()(backed by_NSZeroData) and grew it viaappend(contentsOf: ArraySlice<UInt8>). The first COW transition triggered abrk #1inside ObjC-bridged value copy during ARC retain of the backing storage.Fix: switch to a raw
UnsafeMutableRawPointerbuffer +Data.append(_:count:), bypassing the Sequence/COW path entirely. The loop is also tightened — break onstreamResult == 0, fail fast when the decoder makes no progress (avoids potentially spinning on truncated input).2) Eject — MachOKit on non-Mach-O files (regression from 8a832b4)
The Unity fallback scan inside
frameworkMachOsInBundlecalledisMachOon every level-2 file inFrameworks/*.framework/— includingInfo.plist,.car,.nib,.bin.MachOKit.loadFromFiletraps (brk #1) insideNSFileHandle.read<A>(offset:swapHandler:)when parsing such inputs, andtry?cannot catch it. Eject crashed insideBundle.swift:72on any app whose frameworks contain non-Mach-O files.Fix:
isMachOis now a cheap file-size + 4-byte magic pre-check covering the 8 Mach-O / fat magic variants. Non-Mach-O files are rejected before MachOKit is ever invoked.3) Eject — MachOKit DyldCache path (regression from 5ea814a)
The
injectedAssetNamesdiff loop calledloadedDylibsOfMachOon every Mach-O with a.troll-fools.baksibling. MachOKit's load-command iteration reachesDyldCache.programsTrieEntries→Sequence.programOffsetsand traps on some binaries.That filter exists to avoid re-selecting a previously-injected dylib as the injection target during inject — it's not needed on eject at all.
Fix:
collectModifiedMachOsno longer routes throughframeworkMachOsInBundle. It does a plain filesystem scan ofFrameworks/for files with a.troll-fools.baksibling, which is everything the eject flow needs. No MachOKit load-command iteration on the eject path.Test plan
Frameworks/*.framework/(Info.plist, .car, .nib)injectedAssetNamesNotes
main@9e2fcaa(current upstreammain). No other changes from the fork are included.🤖 Generated with Claude Code